local skynet = require "skynet"
local socket = require "skynet.socket"
local manager = require 'skynet.manager'
local kcp = require 'lkcp'

local convDict = {}
local avatar2ConvDict = {}
local NEXTCONV = 1

local CMD = {}
local SOCKET = {}

local host
function CMD.start(port)
    local onRecvUdp = function(str, from)
        local conv = string.unpack('>I', str)
        if convDict[conv] then
            local convItem = convDict[conv]
            convItem.from = from
            convItem.kcpObj:lkcp_input(str)
            convItem.nextUpdateTime = -1

            local messages = nil
            while true do
                local len, message = convItem.kcpObj:lkcp_recv()
                --没有收到包就退出
                if len <= 0 then
                    break
                end
                local gameProtocol, reason = protobuf.decode('Protocol', message)
                local msgID = gameProtocol.messageType
                local content = gameProtocol.data
                local requestID = gameProtocol.requestID
                if messages == nil then messages = {} end
                table.insert(messages, {msgID, requestID, content})
            end

            if messages then
                local avatar = convItem.avatar
                skynet.send(avatar, 'lua', 'OnRecvKcpMsg', messages)
            end
        end
    end

    host = socket.udp(function(str, from)
        onRecvUdp(str, from)
    end , "0.0.0.0", port)	-- bind an address

    --启动kcp循环
    skynet.fork(function()
        while true do
            local time = math.floor(skynet.time() * 1000)
            for _, item in pairs(convDict) do
                if item.nextUpdateTime <= time then
                    item.kcpObj:lkcp_update(time)
                    item.nextUpdateTime = item.kcpObj:lkcp_check(time)
                end
            end
            skynet.sleep(1)
        end
    end)
end

function CMD.CreateSession(conf)
    local avatar = conf.avatar
    local conv = NEXTCONV
    NEXTCONV = NEXTCONV + 1
    local convItem
    convItem = {
        avatar = avatar,
        from = nil,
        nextUpdateTime = -1,
        kcpObj = kcp.lkcp_create(conv, function (buf) if convItem.from then socket.sendto(host, convItem.from, buf) end end)
    }
    convDict[conv] = convItem
    avatar2ConvDict[avatar] = conv
    return true, conv
end

function CMD.CloseSession(avatar)
    local conv = avatar2ConvDict[avatar]
    avatar2ConvDict[avatar] = nil
    convDict[conv] = nil
    return true
end

function SOCKET.SendMessage(conv, str)
    if convDict[conv] then
        local convItem = convDict[conv]
        convItem.kcpObj:lkcp_send(str)
    end
end

skynet.start(function()
    skynet.dispatch("lua", function(_, _, cmd, subcmd,  ...)
        if cmd == 'CMD' then
            local f = assert(CMD[subcmd])
            skynet.ret(skynet.pack(f(...)))
        elseif cmd == 'SOCKET' then
            local f = assert(SOCKET[subcmd])
            f( ...)
        end
    end)
    manager.register('kcpGate')
end)
